/* Server Buddy
* (AMXX Server-Side Plugin)
*
*  written by Sparkz (Server Buddy)
*
*  This program is free software; you can redistribute it and/or modify it
*  under the terms of the GNU General Public License as published by the
*  Free Software Foundation; either version 3 of the License, or (at
*  your option) any later version.
*
*  This program is distributed in the hope that it will be useful, but
*  WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
*  General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software Foundation,
*  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*  In addition, as a special exception, the author gives permission to
*  link the code of this program with the Half-Life Game Engine ("HL
*  Engine") and Modified Game Libraries ("MODs") developed by Valve,
*  L.L.C ("Valve"). You must obey the GNU General Public License in all
*  respects for all of the code used other than the HL Engine and MODs
*  from Valve. If you modify this file, you may extend this exception
*  to your version of the file, but you are not obligated to do so. If
*  you do not wish to do so, delete this exception statement from your
*  version.
*/

// *****************************
// INCLUDES
// *****************************

#include <amxmodx> // Required base for amxx plugins to work
#include <amxmisc> // Misc AMXX functions
#include <fakemeta> // Fake Metamod (Allows function forwarding and tapping directly into and over-riding game engine functions)
#include <fakemeta_util> // Fake Metamod Utility Functions (Replace engine & fun module)
#include <cstrike> // Counter-Strike specific functions


// *****************************
// FUNCTION MACROS
// *****************************


	/* -------- FILE MANIPULATION MACROS --------- */

#define create_file(%1,%2) funcCreateFile(%1,%2) // Create a new file (FileToCreate, ContentsOfNewFile)
#define show_file(%1,%2) funcShowFile(%1,%2) // Display a file in a players console (PlayerID, FileToShow)

	
	/* -------- MESSAGING MACROS --------- */

#define say_green(%1,%2) funcSayGreen(%1,%2) // Display green text (id, TextToDisplay) id = 0 = all players
#define say_green_admin(%1) funcSayGreen(100,%1) // Display green text (TextToDisplay)
#define msg_admin(%1) funcAdminConsole(%1) // Print a message to an admin's console (MsgText)

	
	/* -------- GENERIC FUNCTION MACROS --------- */

#define flag_player(%1,%2) funcFlag(%1,%2,1) // Flag a player (id, adminID)
#define request_player(%1,%2) funcFlag(%1,%2,2) // Request a player scan (id, adminID)
#define remove_flag(%1,%2) funcRemoveFlag(%1,%2,1) // Remove a flag from file (SteamID, id)
#define remove_request(%1,%2) funcRemoveFlag(%1,%2,2) // Remove a request from file (SteamID, id)

#define symbols_in_name(%1) funcCheckName(%1) // Check for anti-kick symbols in a name


	/* -------- PLAYER INFO MACROS --------- */

#define is_user_flagged(%1) funcIsPlayerFlagged(%1,"",1) // Check if a player by ID is flagged
#define is_steam_flagged(%1) funcIsPlayerFlagged(0,%1,1) // Check if a STEAM_ID is flagged
#define is_user_requested(%1) funcIsPlayerFlagged(%1,"",2) // Check if a player by ID is requested
#define is_steam_requested(%1) funcIsPlayerFlagged(0,%1,2) // Check if a STEAM_ID is requested


	/* -------- LOGGING MACROS --------- */

#define log_warning(%1) funcLog(xFileWarnings,%1) // Log to the warnings log file
#define log_detection(%1) funcLog(xFileDetections,%1) // Log to the detections log file
#define log_activity(%1) funcLog(xFileActivity,%1) // Log to the activity log file


// *****************************
// GLOBAL CONSTANTS
// *****************************


	/* -------- PLUGIN INFO --------- */

new const plugin_name[] = "Server Buddy"; // Plugin Name
new const plugin_version[] = "4.0"; // Plugin Version
new const plugin_author[] = "Sparkz"; // Plugin Author


	/* -------- CVAR DEFAULTS --------- */

new const owner_name[] = "Your Name"; // The owners name. Will be used as the license name within Server Buddy scanners that connect to your game server.
new const owner_website[] = "www.yoursite.com"; // URL where scanner can be downloaded (don't include http: as a prefix)
new const owner_contact[] = "my.serverbuddy.info"; // URL where users can find out more information about Server Buddy
new const owner_email[] = "scans@yoursite.com"; // Email address that users will send their scans to.


	/* -------- INTERNAL SETTINGS --------- */

#define CAN_SHOOT 	1 // 1 = Player randomly misses his shots, 0 = Shoots all blanks
#define RANDOM_SHOTS 	7 // Used to adjust the sensitivity of how many times a flagged player shoots blanks. Higher number = higher miss rate.
#define NUMBER_OF_HACKS 8 // Used throughout the plugin to loop through hack information
#define MAX_CLR 	10 // Used throughout the plugin to loop through colors

	
	/* -------- FILE LOCATIONS --------- */

new const FILE_FLAGS[] =  "ServerBuddy/flags.ini"; // Flagged players are stored in this file
new const FILE_HACKLOG[] =  "ServerBuddy/hack_detections.log"; // All bans are logged to this file
new const FILE_WARNLOG[] = "ServerBuddy/players_warned.log"; // When players have been warned about their account it is logged in this file.
new const FILE_ADMINLOG[] = "ServerBuddy/admin_activity.log"; // All admin activity is logged in this file.
new const FILE_REQUEST[] = "ServerBuddy/requests.ini"; // Requested players are stored in this file
new const FILE_TEMPREQUEST[] = "ServerBuddy/sbuddyr.tmp"; // Temporary garbage file (needed to un-request players)
new const FILE_TEMP[] = "ServerBuddy/sbuddy.tmp"; // Temporary garbage file (needed to unflag players)

new const FILE_NOTE_FLAGGED[] = "ServerBuddy/notes/flagged.txt"; // Information shown to player regarding restrictions and instructions on removal
new const FILE_NOTE_REQUESTED[] = "ServerBuddy/notes/requested.txt"; // Information shown to player about current request to scan
new const FILE_NOTE_GHOSTING[] = "ServerBuddy/notes/ghosting.txt"; // Information shown to player about ghosting restrictions



	/* -------- MISCELLANEOUS --------- */

#define TASK_ID 1337 // Unique task identifier to be used with indivdual (per-player) timers [Ghosting Function]
#define TASK_SHOTS_ID 1447 // Unique task identifier to be used with indivdual (per-player) timers [Random Blanks]

	
	/* -------- SPEAK FLAGS --------- */

#define SPK_NORMAL		0
#define SPK_MUTED 		1
#define SPK_ALL  		2
#define SPK_LISTENALL 		4
#define SPK_ADMIN 		5


	/* -------- HITBOX FLAGS --------- */

#define HITGROUP_GENERIC	0 
#define HITGROUP_HEAD		1 
#define HITGROUP_CHEST		2 
#define HITGROUP_STOMACH	3 
#define HITGROUP_LEFTARM	4 
#define HITGROUP_RIGHTARM	5 
#define HITGROUP_LEFTLEG	6 
#define HITGROUP_RIGHTLEG	7 


// *****************************
// GLOBAL VARIABLES
// *****************************


	/* -------- PLUGIN INFO --------- */

new maxPlayers; // Stores the server's player max + 1 for use in loop structures
new bool:isHalfLife = true; // Track what mod the server is running


new xDir[200]; // Stores the path of the configs folder


	/* -------- FILE PATHS --------- */
	
new xFileFlags[200],xFileDetections[200],xFileWarnings[200],xFileActivity[200],xFileTemp[200];
new xFileRequest[200],xFileTempRequest[200],xFileNoteGhost[200],xFileNoteFlag[200],xFileNoteRequest[200];


	/* -------- PLAYER INFORMATION --------- */
	
new forcedName[33]; // Stores the name a player was flagged under.
	
new bool:is_flagged[33] = false; // Is the player flagged?
new bool:is_real_player[33] = false; // Is the player real? Connected, Not a bot, Not HLTV
new bool:requested[33] = false; // Was the player asked to scan?
new bool:is_ghosting[33] = false; // Keep track of who is ghosting
new bool:is_admin[33] = false; // Is the player an admin? (used in loops to avoid  multiple lookups)

new Float:msgLastFlag[33] = 0.0; // Track how long in-between a player has viewed an external file (spam control)
new Float:msgLastRequest[33] = 0.0; // Track how long in-between a player has viewed an external file (spam control)
new Float:msgLastGhost[33] = 0.0; // Track how long in-between a player has viewed an external file (spam control)


new xSpeak[33]; // Current speak channel

new bodyShots[33][33]; // Keep track of hitbox locations for each player. (Where are the shots hitting?)


	/* -------- CVAR POINTERS --------- */
	
new xcvar_sbuddy_amxbans; // Ban players using the amxbans method? 1 = yes ,0 = no
new xcvar_sbuddy_antikick; // Kick players that have anti-kick symbols in their name? 1 = yes ,0 = no
new xcvar_sbuddy_banplayer; // Ban players when a detection is made? 1 = yes ,0 = no (just log it)
new xcvar_sbuddy_crashguard; // Prevent players from crashing the server by abusing the fullupdate command? 1 = yes ,0 = no
new xcvar_sbuddy_ghostcheck; // Check players to see if they are playing using the same IP? 1 = yes ,0 = no
new xcvar_sbuddy_notifyadmins; // When a player does something or something happens should we notify admins? 1 = yes,0 = no
new xcvar_sbuddy_protect; // Check for flagged players when they connect? 1 = yes ,0 = no
new xcvar_sbuddy_scanner; // Scan players for old hack setinfo lines? 1 = yes ,0 = no
new xcvar_sbuddy_showbans; // Display bans to players in game? 1 = yes ,0 = no
new xcvar_sbuddy_website; // Stores the scanner location


	/* -------- HALF-LIFE MESSAGE ID's --------- */
	
new xFade; // ScreenFade Reference	
new xSayText; // Player say text

	
	/* -------- SCROLLING MESSAGE --------- */
	
new Float:sc_x_pos;
new sc_start_pos; 
new sc_end_pos; 
new sc_scroll_msg[384];
new sc_display_msg[384]; 
new sc_a;  
new sc_timer; 
new sc_length; 

new ncolors[MAX_CLR][] = {"white","red","green","blue","yellow","magenta","cyan","maroon","ocean","orange"};
new vcolors[MAX_CLR][3] = {{255,255,255},{255,0,0},{0,255,0},{0,0,255},{255,255,0},{255,0,255},{0,255,255}, {227, 96, 8}, {45, 89, 116}, {103, 44, 38}} ;  


	/* -------- SETINFO HACK INFO --------- */
	
new xHackList[NUMBER_OF_HACKS][] = {"TeKilla","MicCheat","EcstaticCheat","LCD","PimP","_PRJVDC","AlphaCheat","Chapman"};
new xHackDescription[NUMBER_OF_HACKS][] = {"TeKilla Cheat","Mic Cheat","Ecstatic Cheat","LCD Cheat","PimP Cheat","VDC Cheat","Alpha Cheat","Chapman Cheat"};


	/* -------- WEAPON RESTRICTIONS --------- */
	

new xAllowedWeaponsHL[2] = {HLW_CROWBAR, HLW_GLOCK}; // HALF-LIFE
new xAllowedWeapons[7] = {CSW_KNIFE, CSW_C4, CSW_DEAGLE, CSW_ELITE, CSW_FIVESEVEN, CSW_GLOCK18, CSW_USP}; // COUNTER-STRIKE


// *****************************
// INITIALIZE PLUGIN
// *****************************
	
public plugin_init() 
{
		/* -------- REGISTER PLUGIN --------- */
	
	register_plugin(plugin_name, plugin_version, plugin_author);
	
	
		/* -------- REGISTER PUBLIC CVARS --------- */	
		
	register_cvar("sbuddy_version", plugin_version, FCVAR_SERVER); // PUBLIC CVAR	
	register_cvar("sbuddy_owner", owner_name, FCVAR_SERVER); // Stores name of Server Owner (Shown in Scanners)	
	register_cvar("sbuddy_email", owner_email, FCVAR_SERVER); // Stores email address of server owner.	
	register_cvar("sbuddy_scancenter", "000000", FCVAR_SERVER); // Used to store & retrieve location of Scan Center	
	register_cvar("sbuddy_key", "000000", FCVAR_SERVER); // License code (activates Server Buddy features)
	
		
		/* -------- REGISTER CVAR POINTERS --------- */
		
	xcvar_sbuddy_amxbans = register_cvar("sbuddy_amxbans", "0");
	xcvar_sbuddy_antikick = register_cvar("sbuddy_antikick", "1");
	xcvar_sbuddy_banplayer = register_cvar("sbuddy_banplayer", "1");	
	xcvar_sbuddy_crashguard = register_cvar("sbuddy_crashguard", "1");	
	xcvar_sbuddy_ghostcheck = register_cvar("sbuddy_ghostcheck", "1");
	xcvar_sbuddy_notifyadmins = register_cvar("sbuddy_notifyadmins", "1");
	xcvar_sbuddy_protect = register_cvar("sbuddy_protect", "1");
	xcvar_sbuddy_scanner = register_cvar("sbuddy_scanner", "1"); 
	xcvar_sbuddy_showbans = register_cvar("sbuddy_showbans", "1");	
	xcvar_sbuddy_website = register_cvar("sbuddy_website", owner_website);	
	
	
		/* -------- REGISTER CONSOLE COMMANDS --------- */
		
	register_concmd("amx_flag", "cmdFLAG", ADMIN_BAN, "<authid, nick or #userid>"); // Flag a player
	register_concmd("amx_unflag", "cmdUNFLAG", ADMIN_BAN, "'<authid>' [enclose authid in quotes]"); // Unflag a player
	register_concmd("amx_request", "cmdREQUEST", ADMIN_BAN, "<authid, nick or #userid>"); // Request that a player scan
	register_concmd("amx_unrequest", "cmdUNREQUEST", ADMIN_BAN, "'<authid>' [enclose authid in quotes]"); // Unrequest a player
	register_concmd("amx_clearflags", "cmdCLEAR", ADMIN_RCON, ""); // Clear all the flagged players
	register_concmd("amx_clearrequests", "cmdCLEAR", ADMIN_RCON, ""); // Clear all the requests
	register_concmd("amx_listflags", "cmdLIST", ADMIN_BAN, ""); // Show all the players flagged	
	register_concmd("amx_listrequests", "cmdLIST", ADMIN_BAN, ""); // Show all the players requested	
	register_concmd("amx_keyhook", "cmdHOOK", ADMIN_RCON, "<authid, nick or #userid>"); // Add our trigger keys to a player to detect delete or insert keypress events
	register_concmd("amx_unkeyhook", "cmdHOOK", ADMIN_RCON, "<authid, nick or #userid>"); // Remove our trigger binds from a player and restore keys to default
	register_concmd("amx_voiceon", "cmdSPEAK", ADMIN_BAN, "<authid, nick or #userid>"); // Listen to a player
	register_concmd("amx_voiceoff", "cmdSPEAK", ADMIN_BAN, "<authid, nick or #userid>"); // Stop listening to a player
	register_srvcmd("sb_scroll", "cmdSCROLL", ADMIN_RCON, "<color> <message>") // Scrolling messages
			
	
		/* -------- REGISTER HOOKS --------- */
			
	register_clcmd("fullupdate","cmdFULLUPDATE"); // Blocks fullupdate command
	register_concmd("+invnext", "cmdKEYPRESS", 0, ""); // A trigger we can use to detect a keypress (bind)
	register_concmd("+invprevious", "cmdKEYPRESS", 0, ""); // A trigger we can use to detect a keypress (bind)
	register_concmd("-invnext", "cmdHANDLE", 0, ""); // Block the command because it is called after the first one is entered (hides error message in player console)
	register_concmd("-invprevious", "cmdHANDLE", 0, ""); // Block the command because it is called after the first one is entered (hides error message in player console)
	
	
		/* -------- REGISTER FORWARDS --------- */
		
	register_forward(FM_ClientUserInfoChanged, "forward_infochanged"); // Player changed something
	register_forward(FM_Voice_SetClientListening, "forward_setlisten"); // Intercept communication (used for restriction purposes)
	
	register_forward(FM_TraceLine,"fw_traceline",1); // Determine where a player is looking and if/what he shot
	
	register_event("CurWeapon", "event_holdwpn", "be", "1=1"); // Player switched weapons, whats he holding?
	register_event("ResetHUD", "event_spawn", "be"); // Catch a player spawn	
	
	
		/* -------- INITIALIZE GLOBAL VARIABLES --------- */
	
	maxPlayers = get_maxplayers() + 1; // Initilize the player max variable
	get_configsdir(xDir,199);
	xFade = get_user_msgid("ScreenFade"); // Reference to screen fade message (Game Engine)
	xSayText = get_user_msgid("SayText"); // Reference to SayText message (Game Engine)
		

		/* -------- CACHE FILE PATHS --------- */
		
	formatex(xFileFlags,199,"%s/%s",xDir,FILE_FLAGS); // Reference our file from our constant
	formatex(xFileDetections,199,"%s/%s",xDir,FILE_HACKLOG); // Reference our file from our constant
	formatex(xFileWarnings,199,"%s/%s",xDir,FILE_WARNLOG); // Reference our file from our constant
	formatex(xFileActivity,199,"%s/%s",xDir,FILE_ADMINLOG); // Reference our file from our constant
	formatex(xFileTemp,199,"%s/%s",xDir,FILE_TEMP); // Reference our file from our constant
	formatex(xFileRequest,199,"%s/%s",xDir,FILE_REQUEST); // Reference our file from our constant
	formatex(xFileTempRequest,199,"%s/%s",xDir,FILE_TEMPREQUEST); // Reference our file from our constant	
	formatex(xFileNoteFlag,199,"%s/%s",xDir,FILE_NOTE_FLAGGED); // Reference our file from our constant
	formatex(xFileNoteRequest,199,"%s/%s",xDir,FILE_NOTE_REQUESTED); // Reference our file from our constant
	formatex(xFileNoteGhost,199,"%s/%s",xDir,FILE_NOTE_GHOSTING); // Reference our file from our constant
	
	
	
		/* -------- GENERATE MISSING FILES --------- */
				
	create_file(xFileFlags, "// Server Buddy Auto Generated File");
	create_file(xFileRequest, "// Server Buddy Auto Generated File"); 
}


// *****************************
// PLUGIN INITIALIZED
// *****************************

public plugin_cfg()
{
	new mod[32] // Store the name of the current mod
	get_modname(mod, 31);
	
	if (containi(mod, "cstrike") != -1) isHalfLife = false; // Change plugin mod
}


// *****************************
// PRE-LOAD REQUIRED FILES
// *****************************

public plugin_precache()
{
		/* -------- HOOK PLAYER CHAT --------- */
			
	register_clcmd("say","cmdSAY");
	register_clcmd("say_team","cmdSAY");
	
			
		/* -------- LOAD REQUIRED SOUNDS --------- */
		
	precache_sound("misc/sb_flagged.wav");
	precache_sound("misc/sb_idiot.wav");	
}


// *****************************
// PLAYER CONNECTS
// *****************************

public client_connect(id)
{	
	if (task_exists(TASK_ID + id)) remove_task(TASK_ID + id); // Is there a timer (to check for ghosting) that exists for this player? If so delete it!
	if (task_exists(TASK_SHOTS_ID + id)) remove_task(TASK_SHOTS_ID + id); // Is there a timer (to punish a player) that exists for this player? If so delete it!		
		
		/* -------- SET PLAYER INFORMATION --------- */
		
	xSpeak[id] = SPK_NORMAL; // Change the players speaking permission to normal	
	is_flagged[id] = false; // Player just connected so they can't be flagged "YET"
	is_ghosting[id] = false; // Player just connected so we don't know yet if they are ghosting
	requested[id] = false; // Player just connected so we aren't sure if we requested them to scan
	is_admin[id] = false; // Player hasn't been authenticated as an admin yet
	
		
		/* -------- SET LAST GAME TIME INFO --------- */
		
	msgLastGhost[id] = 0.0; // Set last game time
	msgLastFlag[id] = 0.0; // Set last game time
	msgLastRequest[id] = 0.0; // Set last game time
		
	cmdGHOSTCHECK(id); // Check to see if this player matches the IP of another player (aka. Ghosting)
	
	
		/* -------- SET HITBOX INFORMATION --------- */
	
	for(new i = 0; i < maxPlayers; i++)
	{
		bodyShots[id][i]  =	(1<<HITGROUP_GENERIC) | (1<<HITGROUP_HEAD) | (1<<HITGROUP_CHEST) | 
					(1<<HITGROUP_STOMACH) | (1<<HITGROUP_LEFTARM) | (1<<HITGROUP_RIGHTARM)| 
					(1<<HITGROUP_LEFTLEG) | (1<<HITGROUP_RIGHTLEG);
	}
}


// *****************************
// PLAYER DISCONNECTS
// *****************************

public client_disconnect(id)
{
	if (task_exists(TASK_ID + id)) remove_task(TASK_ID + id); // Is there a timer (to check for ghosting) that exists for this player? If so delete it!
	if (task_exists(TASK_SHOTS_ID + id)) remove_task(TASK_SHOTS_ID + id); // Is there a timer (to punish a player) that exists for this player? If so delete it!		

	is_real_player[id] = false; // Player isn't real anymore, sigh. lol
	is_ghosting[id] = false; // Player can't be ghosting if they aren't even on the server (set to false)
	
	xSpeak[id] = SPK_NORMAL; // Reset speaking channel
	
	// Maybe the player that disconnected "WAS" ghosting. Which means another player on the server
	// was ghosting as well. We should re-check each player and give them back certain
	// privelledges if they are no longer ghosting. It's only fair ;)	
	new xCount, xPlayers[32];
	get_players(xPlayers,xCount,"cgh"); // Get an array of all players (No Bots, No HLTV, Case Insensitive)
				
	for(new i=0;i<xCount;i++) // Loop through our players one by one
	{		
		if (is_ghosting[i])	// Only check the players that were ghosting originally and aren't bots
		{			
			is_ghosting[i]=false; // Assume they aren't ghosting for the time being
			cmdGHOSTCHECK(i); // Check to see if they are still ghosting
		}
	}
}


// *****************************
// STEAM_ID VALIDATED
// *****************************

public client_authorized(id)
{

		/* -------- GATHER INFORMATION ABOUT PLAYER --------- */

	if(get_user_flags(id) & ADMIN_KICK) is_admin[id] = true; // Player is an admin (tell the plugin)
	
	new xID = get_user_userid(id); // Store the players userid

	new xName[32], xSteam[32];
			
	get_user_authid(id,xSteam,31); // Store players steamid
	get_user_name(id,xName,31); // Store players name
	
		/* :::::::::: CORE CODE :::::::::: */
	
	if(callfunc_begin("playerConnect","serverbuddy_core.amxx") == 1) // Call the playerConnect function in the "core"
	{     
		callfunc_push_int(id); // Pass the player ID to the function in "core"
		callfunc_end(); // Terminate call session
	}
	
		
		/* -------- NAME CHECK --------- */		
		
	if(get_pcvar_num(xcvar_sbuddy_antikick) != 0 && symbols_in_name(xName)) // Should we check for anti-kick symbols?
	{	
		server_cmd("kick #%d ^"(Remove the ` or ~ from your NAME): Anti-kick symbols are restricted!^"", xID); // Kick them & tell them why
		if(get_pcvar_num(xcvar_sbuddy_showbans) != 0) client_print(0,print_chat,"%s has been kicked for using anti-kick symbols in his name!",xName); // Should we tell the other players what just happened?	
	}
	
	
		/* -------- CHECK FOR FLAGGED PLAYERS --------- */
		
	if(get_pcvar_num(xcvar_sbuddy_protect) != 0) // Are we checking for flagged or requested players?
	{	
		
			/* -------- FLAGGED --------- */
			
		if (is_user_flagged(id)) // Check to see if the player is in our flagged players file
		{			
			is_flagged[id] = true; // Looks like they are so tell the rest of the plugin that this player is flagged
			client_cmd(id, "name ^"%s^"", forcedName[id]); // Make sure they use the name they were flagged with (FORCE IT)
			
			new passID[1]; // Store the ID of our player and pass it to our timers
																
			if(!task_exists(TASK_SHOTS_ID + id)) // Make sure we don't already have a timer to punish our player (random blanks)
			{
				passID[0] = id; // Store player ID			
				set_task(2.0, "timer_SHOTS", TASK_ID + id, passID, 1, "b"); // Lets setup a personalized timer for this player to run checks on that player and apply restrictions
			} 
			
			server_cmd("sb_scroll green **FLAGGED** The Account of [%s] has been Flagged. They Cannot Speak! **FLAGGED**",xName); // Create a new scrolling msg
			
			new xPassLine[200];
			formatex(xPassLine,199,"[WARNED] ---- %s ---- (%s) --- was warned about his account!",xName,xSteam); // Create our log line
			log_warning(xPassLine); // Log it
		} else
		{
			is_flagged[id] = false; // Player isn't flagged so tell the plugin he's not flagged.
		}	
		
		
			/* -------- REQUESTED --------- */
			
		if (is_user_requested(id)) // Check to see if the player is in our requests file
		{			
			requested[id] = true; // Looks like they are so tell the rest of the plugin that this player has been requested to scan

			new xPassLine[200];
			formatex(xPassLine,199,"[NOTICE] ---- %s ---- (%s) --- was requested to scan and was notified (not flagged)!",xName,xSteam); // Create our log line
			log_warning(xPassLine); // Log it
		} else
		{
			requested[id] = false; // Player hasn't been requested to scan so tell the plugin
		}
			
	}	
	return PLUGIN_CONTINUE
}


// *****************************
// PLAYER INITIATED INTO GAME
// *****************************

public client_putinserver(id)
{
	
	if(is_user_connected(id) && !is_user_bot(id) && !is_user_hltv(id)) is_real_player[id] = true; // Player is real, sweet, now we can avoid pointless checks in the future

		/* -------- NAME CHECK --------- */
		
	if (is_flagged[id]) 
	{		
		client_cmd(id, "name ^"%s^"", forcedName[id]); // Make sure they use the name they were flagged with (FORCE IT)	
		new info = engfunc(EngFunc_GetInfoKeyBuffer, id);
		engfunc(EngFunc_SetClientKeyValue, id, info, "name", forcedName[id]); // Change the name in their info structure to the name we will force them to use
       	}
	
	
		/* -------- SETINFO HACK CHECK --------- */
		
	if(get_pcvar_num(xcvar_sbuddy_scanner) != 0) // Scan them for old hack setinfo lines?
	{	
		new xInfoLine[5];
		new xUserID = get_user_userid(id);
		
		for(new i=0;i<NUMBER_OF_HACKS;i++) // Loop through our list of known setinfo lines
		{ 			
			get_user_info(id,xHackList[i],xInfoLine,4); // Check our player for the setinfo line				
			
			if(strlen(xInfoLine) > 0) // If the length of our return is greater than 0 then the setinfo line was present with a value.
			{
				new xSteam[32], xName[32], xReason[100], xPassLine[200];
				
				get_user_authid(id,xSteam,31);
				get_user_name(id, xName, 31);
				
				formatex(xReason,99,"Server Has Detected Traces of [%s]", xHackDescription[i]); // Tell the player why he's been banned.			
								
				formatex(xPassLine,199,"[HACK] ---- %s ---- (%s) ---- (%s)",xName,xSteam,xHackDescription[i]); // Create our log line
				log_detection(xPassLine); // Log it
				
				if(get_pcvar_num(xcvar_sbuddy_banplayer) != 0) // Should we ban the player?
				{
					if(get_pcvar_num(xcvar_sbuddy_amxbans) != 0) // Are we using amxbans?
					{
						server_cmd("amx_ban 0 %s Server Buddy: %s",xSteam,xHackDescription[i]);
					} else
					{											
						server_cmd("kick #%d ^"%s (Banned Permanently)^";wait;banid ^"0.0^" ^"%s^";wait;writeid", xUserID, xReason, xSteam);
					}
					
					
					if(get_pcvar_num(xcvar_sbuddy_showbans) != 0) // Do we tell the other players we just banned a player?
					{
						server_cmd("sb_scroll red **BANNED** Server has detected a MULTI-HACK on %s [%s] **BANNED**",xName,xSteam); // Create a new scrolling msg
								
						client_cmd(0,"spk ^"%s^"","misc/sb_idiot.wav"); // Play the 'you are an idiot' sound
					}
				}
			}			
		}
	}
}


// *****************************
// PLAYER SPAWNED
// *****************************

public event_spawn(id)
{

		/* -------- SET GLOW ON SPAWN --------- */

	if (is_flagged[id] && is_user_alive(id)) fm_set_rendering(id,kRenderFxGlowShell,255,0,0,kRenderNormal,65); // If the user is alive we are going to make them glow red!		
	if (requested[id] && is_user_alive(id) && !is_flagged[id]) fm_set_rendering(id,kRenderFxGlowShell,0,0,255,kRenderNormal,25); // If the user is alive we are going to make them glow blue!	

	if (!is_user_alive(id)) return; // Ignore dead players
	

		/* -------- NAG PLAYERS TO SCAN --------- */
	
	if (requested[id])
	{	
		set_hudmessage(85, 255, 255, -1.0, -1.0, 0, 6.0, 12.0); // Format a screen message to be displayed to our player
		show_hudmessage(id, "You have a Pending Scan Request!"); // Tell the player they have been flagged
		
		say_green(id, " An admin has requested you submit a scan. Please See CONSOLE for Details!"); // Notification (green)
		
		new Float:gameTime = get_gametime(); // Store time elapsed
		new Float:msgLimit = 120.0; // Store time in-between sessions you wish to allow (2 minutes)
    		    		    				
		if(msgLastRequest[id] <= gameTime) // Check if we should display the file again
		{
			client_cmd(id,"Clear"); // Clear the console		
			if(!show_file(id, xFileNoteRequest)) log_amx("Server Buddy: Error opening %s",xFileNoteRequest); // Show instructions
			
			msgLastRequest[id] = gameTime + msgLimit; // Remember the last time we showed our file to this player
		}
	}

	
		/* -------- GIVE PLAYER A MEELY WEAPON --------- */
	
	if (!is_flagged[id]) return; // Ignore below code if player isn't flagged

	if (isHalfLife)
	{
		fm_give_item(id, "weapon_crowbar"); // Give the player a knife (just in case)
	}else
	{
		fm_give_item(id, "weapon_knife"); // Give the player a knife (just in case)
	}
	return
}


// *****************************
// PROCESS PLAYER CHAT
// *****************************

public cmdSAY(id)
{		
	if (!is_flagged[id]) return PLUGIN_CONTINUE; // Ignore below code if player isn't flagged

	static xName[33]; // Store the players name
	get_user_name(id, xName, 32);
	
	
		/* -------- READ WHAT WAS SAID --------- */
	
	static said[129]; // Storage for what the player said
	read_args(said,128); // Read what the player said
	remove_quotes(said); // Cleanup what was said
	
	if (containi(said,"Flagged") == -1) // If what they said has the word 'flagged' in it don't execute the below code (avoids endless loop)
	{
	
			/* -------- SEND MESSAGE TO ADMINS --------- */
		
		new xMsg[129]; // Message to send to admins
		
		if (!is_user_alive(id))	add(xMsg,128,"*DEAD*"); // Add *DEAD* if player is dead
		add(xMsg,128,"(Flagged) "); // Add the flagged tag
		add(xMsg,128,xName); // Add the players name
		add(xMsg,128," :  "); // Add seperator for chat text
		add(xMsg,128,said); // Add what the player said				
	
		if(get_pcvar_num(xcvar_sbuddy_notifyadmins) != 0) say_green_admin(xMsg); // Send to admins only		
		say_green(id, "Your Account has been Flagged! See CONSOLE for Details!"); // Notification
								
		client_cmd(id,"spk ^"%s^"","misc/sb_flagged.wav"); // Play the flagged account sound
		
		new Float:gameTime = get_gametime(); // Store time elapsed
		new Float:msgLimit = 30.0; // Store time in-between sessions you wish to allow (30 Seconds)
    		    		    				
		if(msgLastFlag[id] <= gameTime) // Check if we should display the file again
		{
			client_cmd(id,"Clear"); // Clear the console		
			if(!show_file(id, xFileNoteFlag)) log_amx("Server Buddy: Error opening %s",xFileNoteFlag); // Show instructions
			
			msgLastFlag[id] = gameTime + msgLimit; // Remember the last time we showed our file to this player
		}
		
	} else
	{
		return PLUGIN_HANDLED; // Block the chat from other players
	}

	return PLUGIN_HANDLED; // Block the chat from other players
}


// *****************************
// PLAYER SWITCHED WEAPONS
// *****************************

public event_holdwpn(id) 
{
		/* -------- SET GLOW WHEN PLAYER SWITCHES WEAPONS --------- */

	if (is_flagged[id] && is_user_alive(id)) fm_set_rendering(id,kRenderFxGlowShell,255,0,0,kRenderNormal,65); // If the user is alive we are going to make them glow red!		
	if (requested[id] && is_user_alive(id) && !is_flagged[id]) fm_set_rendering(id,kRenderFxGlowShell,0,0,255,kRenderNormal,25); // If the user is alive we are going to make them glow blue!
	
	if (!is_flagged[id] || !is_user_alive(id)) return PLUGIN_CONTINUE; // Is the player flagged or better yet, alive? If not then don't bother with the below code.
	
	new weaponType = read_data(2); // Read what weapon the player is holding


			/* -------- CHECK FOR VALID WEAPON CHOICE --------- */
			
	if(isHalfLife)
	{
		for (new i = 0; i < 2; i++) if (weaponType == xAllowedWeaponsHL[i]) return PLUGIN_CONTINUE; // Is the player allowed to use that weapon? Lets loop through our allowed weapons and see!
		
		engclient_cmd(id, "weapon_crowbar"); // HALF-LIFE
		say_green(id, "You may ONLY use the Crowbar or Glock! Your Account is Flagged!"); // Tell the player whos the boss!   
		
		return PLUGIN_CONTINUE;
	} else
	{
		for (new i = 0; i < 7; i++) if (weaponType == xAllowedWeapons[i]) return PLUGIN_CONTINUE; // Is the player allowed to use that weapon? Lets loop through our allowed weapons and see!
		
		engclient_cmd(id, "weapon_knife"); // COUNTER-STRIKE
		say_green(id, "You may ONLY use Handguns! Your Account is Flagged!"); // Tell the player whos the boss!	   
		
		return PLUGIN_CONTINUE;
	} 	
	
	return PLUGIN_CONTINUE
}


// *****************************
// AMX_FLAG
// *****************************

public cmdFLAG(id, level, cid) 
{
	if (!cmd_access(id, level, cid, 2)) return PLUGIN_HANDLED;

	new xName[32], xSteam[32];
	read_argv(1, xName, 31);

	new player = cmd_target(id, xName, 8); // Find the player
	if (player) 
	{	
		get_user_name(player,xName,31);
		get_user_authid(player,xSteam,31);
		
		if(is_user_flagged(player)) // Use our function to see if they are already flagged
		{
			console_print(id,"%s is already flagged!", xName); 
			return PLUGIN_HANDLED;
		} else
		{
			if(flag_player(player, id) != 1) // Use our function to add the player to our flagged players file. Did it succeed?
			{
				console_print(id,"Error: Unable to write to: %s", xFileFlags);
				return PLUGIN_HANDLED;
			}			
			
		}				
		return PLUGIN_HANDLED;	
	} 
	return PLUGIN_HANDLED;
}


// *****************************
// AMX_REQUEST
// *****************************

public cmdREQUEST(id, level, cid) 
{
	if (!cmd_access(id, level, cid, 2)) return PLUGIN_HANDLED;

	new xName[32], xSteam[32];
	read_argv(1, xName, 31);

	new player = cmd_target(id, xName, 8); // Find the player
	if (player) 
	{	
		get_user_name(player,xName,31);
		get_user_authid(player,xSteam,31);
		
		if(is_user_requested(id)) // Use our function to see if they are already in our requests file
		{
			console_print(id,"%s has already been requested to scan! Use amx_listrequests for details.", xName); 
			return PLUGIN_HANDLED;
		} else
		{
			if(request_player(player, id) != 1) // Use our function to add the player to our flagged players file. Did it succeed?
			{
				console_print(id,"Error: Unable to write to: %s", xFileRequest);
				return PLUGIN_HANDLED;
			}			
			
		}				
		return PLUGIN_HANDLED;
	} 
	return PLUGIN_HANDLED;
}


// *****************************
// AMX_UNFLAG
// *****************************

public cmdUNFLAG(id, level, cid) 
{
	if (!cmd_access(id, level, cid, 2)) return PLUGIN_HANDLED;
	
	new xSteam[32];
	
	read_argv(1, xSteam, 31);
	
	if(is_steam_flagged(xSteam)) // Use our function to check if they are in our flagged players file
	{	
		remove_flag(xSteam, id); // Tell our function to remove the player
			
	} else // Couldn't find the STEAM_ID in our flagged players file. Maybe a typo?
	{
		console_print(id,"%s cannot be found or is not currently flagged!", xSteam);
		return PLUGIN_HANDLED;
	}
	return PLUGIN_HANDLED;
}


// *****************************
// AMX_UNREQUEST
// *****************************

public cmdUNREQUEST(id, level, cid) 
{
	if (!cmd_access(id, level, cid, 2)) return PLUGIN_HANDLED;
	
	new xSteam[32];
	
	read_argv(1, xSteam, 31);
	
	if(is_steam_requested(xSteam)) // Use our function to check if they are in our requests file
	{	
		remove_request(xSteam, id); // Tell our function to remove the player
			
	} else // Couldn't find the STEAM_ID in our requests file. Maybe a typo?
	{
		console_print(id,"%s cannot be found or wasn't requested to scan!", xSteam);
		return PLUGIN_HANDLED;
	}
	return PLUGIN_HANDLED;
}


// *****************************
// AMX_LISTFLAGS & AMX_LISTREQUESTS
// *****************************

public cmdLIST(id, level, cid) 
{
	if (!cmd_access(id, level, cid, 1)) return PLUGIN_HANDLED;
	
	new handleREAD, xCmd[25];
	read_argv(0, xCmd, 24); // Store command
	
	if(equali(xCmd, "amx_listflags"))
	{	
		handleREAD = fopen(xFileFlags, "rt"); // File pointer to our flagged file
		if (!handleREAD) return PLUGIN_HANDLED; // Uh oh, error opening the file
		
		console_print(id, "^n^n------- :: SERVER BUDDY PLAYER FLAGS :: --------^n"); // Display a heading before we display the file
	} else if (equali(xCmd, "amx_listrequests"))
	{
		handleREAD = fopen(xFileRequest, "rt"); // File pointer to our flagged file
		if (!handleREAD) return PLUGIN_HANDLED; // Uh oh, error opening the file

		console_print(id, "^n^n------- :: SERVER BUDDY PLAYER SCAN REQUESTS :: --------^n"); // Display a heading before we display the file
	} else
	{
		return PLUGIN_HANDLED;
	}
	
	new xCurrentLine[2048], xLineSteamID[33], xLineName[33], xFlagCode[8];
		
	while (!feof(handleREAD)) // Loop until we reach the end of our flagged file
	{
		fgets(handleREAD, xCurrentLine, 2047); // Read the current line
		
		if(xCurrentLine[0] == ';') continue; // Ignore comments
		if(xCurrentLine[0] == '/' && xCurrentLine[1] == '/') continue; // Ignore comments		
		
		if(containi(xCurrentLine,"STEAM_") != -1) // If we found a STEAM_ID then list it.
		{
			parse(xCurrentLine, xLineSteamID, 32, xLineName, 32, xFlagCode, 7); // Split the line into 2 seperate variables so we can seperate the players ID from their name
			trim(xLineSteamID); // Cleanup (spaces)
			trim(xLineName); // Cleanup (spaces)
			console_print(id, "%s^t(%s)^t Flag/Request Code: (%s)", xLineSteamID, xLineName, xFlagCode); // Send the information to the admin's console
		}		
	}
	return PLUGIN_HANDLED;
}


// *****************************
// AMX_CLEARFLAGS & AMX_CLEARREQUESTS
// *****************************

public cmdCLEAR(id, level, cid) 
{
	if (!cmd_access(id, level, cid, 1)) return PLUGIN_HANDLED;
	
	new xCmd[25], xPassLine[200], adminName[32], adminSteam[32];
	
	read_argv(0, xCmd, 24); // Store command
	get_user_name(id,adminName,31);
	get_user_authid(id,adminSteam,31);
	
	if(equali(xCmd, "amx_clearflags"))
	{	
		if(file_exists(xFileFlags))
		{
			delete_file(xFileFlags); // Delete the flagged players file
			create_file(xFileFlags, "// Server Buddy Auto Generated Flags File^n"); // Create a new file
			
			console_print(id,"All Flags have been Cleared!");
			
			formatex(xPassLine,199,"[CLEAR LIST] ---- %s ---- (%s) ---- has cleared all player flags",adminName,adminSteam); // Create our log line			
			log_activity(xPassLine); // Log it
		
		} else // File didn't exist, lets create it
		{
			create_file(xFileFlags, "// Server Buddy Auto Generated Flags File^n"); // Create a new file
			console_print(id,"New File Created! [DIDN'T EXIST]");
		}
	} else if (equali(xCmd, "amx_clearrequests"))
	{
		if(file_exists(xFileRequest))
		{
			delete_file(xFileRequest); // Delete the requests files
			create_file(xFileRequest, "// Server Buddy Auto Generated Flags File^n"); // Create a new file
			
			console_print(id,"All Player Scan Requests have been Cleared!");		
		
			formatex(xPassLine,199,"[CLEAR REQUEST LIST] ---- %s ---- (%s) ---- has cleared the request list",adminName,adminSteam); // Create our log line			
			log_activity(xPassLine); // Log it
		
		} else // File didn't exist, lets create it
		{
			create_file(xFileRequest, "// Server Buddy Auto Generated Flags File^n"); // Create a new file
			console_print(id,"New File Created! [DIDN'T EXIST]");
		}
	}		
		
	return PLUGIN_HANDLED;
}


// *****************************
// AMX_KEYHOOK & AMX_UNKEYOOK
// *****************************

public cmdHOOK(id, level, cid) 
{
	if (!cmd_access(id, level, cid, 2)) return PLUGIN_HANDLED;
	
	new xName[32], xCmd[25], xPassLine[200], adminName[32], adminSteam[32];
	
	read_argv(0, xCmd, 24); // Store command
	read_argv(1, xName, 32); // Read player argument

	get_user_name(id,adminName,31);
	get_user_authid(id,adminSteam,31);

	new player = cmd_target(id, xName, 8); // Find the player by name, (don't check bots)
	if (player) 
	{
		new playerName[32], playerSteam[32];
		get_user_name(player, playerName, 31);
		get_user_authid(player, playerSteam, 31);
		
		if (equal(xCmd, "amx_keyhook"))
		{		
			client_cmd(player, "bind INS +invnext"); // Bind their key with our trigger bind
			client_cmd(player, "bind DEL +invprevious"); // Bind their key with our trigger bind
			console_print(id,"Hooked 'insert' and 'delete' keys of: %s", playerName);
			
			formatex(xPassLine,199,"[KEY HOOK] ---- %s ---- (%s) Key hooks added by: %s ---- (%s)",playerName,playerSteam,adminName,adminSteam); // Create our log line
			log_activity(xPassLine); // Log it
		} else if (equal(xCmd, "amx_unkeyhook"))
		{
			client_cmd(player, "bind INS +klook"); // Bind their key to default
			client_cmd(player, "unbind DEL"); // Bind their key to default	
			console_print(id,"Restored hooked keys of %s to their default value.", playerName);
			
			formatex(xPassLine,199,"[REMOVE KEY HOOK] ---- %s ---- (%s) Key hooks removed by: %s ---- (%s)",playerName,playerSteam,adminName,adminSteam); // Create our log line
			log_activity(xPassLine); // Log it
		}
			
		return PLUGIN_HANDLED;
	} 
	return PLUGIN_HANDLED;
}


// *****************************
// AMX_VOICEON & AMX_VOICEOFF
// *****************************

public cmdSPEAK(id, level, cid)
{
	if (!cmd_access(id, level, cid, 2)) return PLUGIN_HANDLED;
	
	new xName[32], xCmd[25];
	read_argv(0, xCmd, 24); // Store command
	read_argv(1, xName, 31);

	new player = cmd_target(id, xName, 8); // Find the player
	if (player) 
	{	
		new xPlayerName[33];
		get_user_name(player,xPlayerName,32); // Store their name
		
		if (equali(xCmd, "amx_voiceon"))
		{	
			xSpeak[id] = SPK_ADMIN; // Change the players speaking channel to the admin channel
			client_cmd(player,"+voicerecord"); // Start the players voice (they might notice)
			console_print(id, "Voice has been activated for: %s (Broadcasting over Admin Channels)", xPlayerName);
		}else if (equali(xCmd, "amx_voiceoff"))
		{
			xSpeak[id] = SPK_NORMAL; // Change the players speaking channel back to normal
			client_cmd(player,"-voicerecord"); // Stop the players voice
			console_print(id, "Voice has been de-activated for: %s", xPlayerName);		
		}
		
		return PLUGIN_HANDLED;
	}
	
	return PLUGIN_HANDLED;
}


// *****************************
// SHOOT BLANKS AT RANDOM
// *****************************

public timer_SHOTS(argID[1]) 
{					
	new id = argID[0]; // Extract players ID from passed arguement
	
	if (is_flagged[id] && is_user_alive(id)) // If they are flagged and alive proceed to the next code block
	{
		new rnum = random_num(0,RANDOM_SHOTS); // Generate a random number between 0 and our defined number (default: 7)
		
		if (rnum <= 2 && CAN_SHOOT == 1) // If the random number is 2 or below and we are allowing players to shoot then let the players shot hit it's target.
		{					
			fm_set_user_hitzones(id, 0, 255); // Player Can Shoot				
		} else 
		{					
			fm_set_user_hitzones(id, 0, 0); // Player Cannot Shoot				
		}
	}	
	
	return PLUGIN_CONTINUE;
}


// *****************************
// BLOCK FULLUPDATE
// *****************************

public cmdFULLUPDATE(id)
{
	new xName[32], xMsg[125];
	get_user_name(id, xName, 31);
	
	formatex(xMsg, 124, "%s issued the fullupdate command", xName); // Create message to send to admins	
	msg_admin(xMsg); // Send message to admin's console
	
	if(get_pcvar_num(xcvar_sbuddy_crashguard) != 0) return PLUGIN_HANDLED; // Block it 
	
	return PLUGIN_CONTINUE; // Don't block it
}


// *****************************
// GENERIC COMMAND BLOCKER
// *****************************

public cmdHANDLE(id) return PLUGIN_HANDLED; // Do nothing just block it!


// *****************************
// KEYPRESS HOOK (INSERT) & (DELETE)
// *****************************

public cmdKEYPRESS(id)
{
	new xName[32], xSteam[32], xCmd[25], xMsg[125], xPassLine[200];
	
	read_argv(0, xCmd, 24); // Store command
		
	get_user_authid(id,xSteam,31); // Who pressed it?
	get_user_name(id,xName,31); // Who pressed it?
	
	if (equali(xCmd, "+invnext"))
	{	
		formatex(xPassLine,199,"[KEY PRESS] ---- %s ---- (%s) ---- Pressed a possible hack toggle key <INSERT>",xName,xSteam); // Create our log line
		formatex(xMsg,124,"%s Pressed the insert key",xName); // Create our message to send to admins
		if(get_pcvar_num(xcvar_sbuddy_notifyadmins) != 0) say_green_admin(xMsg);
	} else if (equali(xCmd, "+invprevious"))
	{
		formatex(xPassLine,199,"[KEY PRESS] ---- %s ---- (%s) ---- Pressed a possible hack toggle key <DELETE>",xName,xSteam); // Create our log line
		formatex(xMsg,124,"%s Pressed the delete key",xName); // Create our message to send to admins
		if(get_pcvar_num(xcvar_sbuddy_notifyadmins) != 0) say_green_admin(xMsg);
	}
	
	log_detection(xPassLine); // Log it
	return PLUGIN_HANDLED; 
}


// *****************************
// CHECK FOR IP MATCHING (GHOSTING)
// *****************************

public cmdGHOSTCHECK(id)
{	
	if(get_pcvar_num(xcvar_sbuddy_ghostcheck) != 0) // Are we looking for ghosters?
	{	
		if (is_real_player[id] && !is_user_admin(id)) // How can a bot be guilty of ghosting?! They cheat enough anyways, lets ignore them and just check players.
		{
			new xIPa[32], xIPb[32]; // Hold the IP Address of the player we are checking and the IP Address of the players we are comparing him against.
			get_user_ip(id,xIPa,31,1); // Get our players IP Address				
						
			for(new i=0;i<maxPlayers;i++) // Loop through our players one by one
			{		
				if (i != id && is_real_player[i] && !is_admin[i]) // Make sure we don't compare our player with himself, we will get a false positive. Careful ;)
				{
					get_user_ip(i,xIPb,31,1) // Store the IP Address of the current player we are going to compare against
					
					trim(xIPa) // Cleanup the IP (remove spaces on either side)
					trim(xIPb) // Cleanup the IP (remove spaces on either side)
					
					if(equali(xIPa,xIPb)) // Looks like our player matches the IP Address of another player
					{	
						is_ghosting[id] = true; // Tell the plugin our player is ghosting
						is_ghosting[i] = true; // Tell the plugin that the player we just compared against is also ghosting
						
							// Display a notification to admins
							
						new xNameA[33], xNameB[33];
						get_user_name(id, xNameA, 32);
						get_user_name(i, xNameB, 32);
		
						for(new ii=0;ii<maxPlayers;ii++) // Loop through our players one by one
						{		
							if (is_user_admin(ii)) // If they are an admin then display the notification
							{
								new xMsg[129]
								formatex(xMsg, 128, "*Server Buddy* (LAN NOTICE) %s and %s have matching IPs", xNameA, xNameB);
								if(get_pcvar_num(xcvar_sbuddy_notifyadmins) != 0) say_green_admin(xMsg); // Notify admins about matching players
							}
						}
						
						new passID[1]; // Store the ID of our player and pass it to our timers
																
						if(!task_exists(TASK_ID + id)) // Make sure we don't already have a timer to check our two players
						{
							passID[0] = id; // Store player ID			
							set_task(0.1, "timer_GHOSTRESTRICT", TASK_ID + id, passID, 1, "b"); // Lets setup a personalized timer for this player to run checks on that player and apply restrictions
						} 
						
						if(!task_exists(TASK_ID + i)) // Make sure we don't already have a timer to check our two players
						{
							passID[0] = i; // Store player ID						
							set_task(0.1, "timer_GHOSTRESTRICT", TASK_ID + i, passID, 1, "b"); // Lets setup a personalized timer for this player to run checks on that player and apply restrictions
						} 						
					}
				}	
			}
			return PLUGIN_CONTINUE;
		}
	}
	return PLUGIN_CONTINUE;
}


// *****************************
// GHOSTER RESTRICTIONS (NO SPEC, NO SEE)
// *****************************

public timer_GHOSTRESTRICT(argID[1])
{
	if (isHalfLife) return PLUGIN_CONTINUE; // Don't execute code below for regular half-life
	
	if(get_pcvar_num(xcvar_sbuddy_ghostcheck) != 0) // Are we checking for ghosters? (players sharing the same IP as another player, aka. lanning)
	{		
		new xID = argID[0]; // Extract their player ID by subtracting our unique ID we added to their ID before passing it to this function
		
		if (is_ghosting[xID]) // If they aren't labelled as ghosting by the plugin then theres no sense running the code blocks below.
		{
		
				/* -------- CHECK IF PLAYER IS A SPECTATOR --------- */
			
			new xTeam = get_user_team_id(xID); // Store which team they are on	
				
			if(xTeam == 3) // Are they on the spectator team?
			{
				say_green(xID, "*Server Buddy* Your IP address matches another players. You are not allowed to enter spectator mode."); // Tell the player whos the boss!	   
					
					// Unknown author of code below used to check which team has less players
				new xCount, xPlayers[32], Float:xOnCT;
				
				get_players(xPlayers,xCount,"g") // Get an array of all players
				
				for(new i=0;i<xCount;i++) if(xPlayers[i] != xID && get_user_team_id(xPlayers[i]) == 2) xOnCT += 1.0; // Lets add a player to our CT Team count (tracking whos on what team)					
				
				if(float(xCount) / 2.0 >= xOnCT) // Lets divide all of our players in half
				{
					cs_set_user_team(xID,CS_TEAM_CT); // If the teams are equal or there are more players on the Terrorist team then put our player on the CT Team (balance)
				} else
				{
					cs_set_user_team(xID,CS_TEAM_T); // Looks like there are more CT then Terrorists so lets add our player to the Terrorist team (balance).
				}
				
				return PLUGIN_CONTINUE
			}
											
											
				/* -------- CHECK IF PLAYER IS SPECTATING AS A DEAD PLAYER --------- */
						
			if((xTeam == 1 || xTeam == 2) && pev(xID, pev_deadflag) == 2) // Our player is dead and in spectate mode as a dead player (still on a team)
			{				
									
				if(pev(xID, pev_iuser1) != 3) // We don't want the player to be able to track players so set their viewmode to free roam (so they aren't locked to a player)
				{
					set_pev(xID, pev_iuser1, 3); // Player tried to change their view, so change it back to free roam. lol Show them whos boss!

					say_green(xID, "*Server Buddy* To find out why your screen is black please check your console!"); // Tell the player whos the boss!	   
					
					new Float:gameTime = get_gametime(); // Store time elapsed
					new Float:msgLimit = 60.0; // Store time in-between sessions you wish to allow (60 Seconds)
			    		    		    				
					if(msgLastGhost[xID] <= gameTime) // Check if we should display the file again
					{
						client_cmd(xID,"Clear"); // Clear the console		
						if(!show_file(xID, xFileNoteGhost)) log_amx("Server Buddy: Error opening %s",xFileNoteGhost); // Show instructions
			
						msgLastGhost[xID] = gameTime + msgLimit; // Remember the last time we showed our file to this player
					}
					
				}
				
					/* -------- BLACKOUT --------- */
									
				message_begin(MSG_ONE, xFade, {0,0,0}, xID); // Turn the players screen black!
				write_short( ~1 ); // Duration
				write_short( ~1 ); // Hold-time
				write_short(1<<12); // Fade Type
				write_byte(0); // Red (00)
				write_byte(0); // Green (00)
				write_byte(0); // Blue (00)
				write_byte(255); // Alpha Transparency
				message_end(); // Phew!! All done.
				
				return PLUGIN_CONTINUE;
			}
		}		
	}
	return PLUGIN_CONTINUE;
}


// *****************************
// FAKEMETA INTERCEPT (TRACELINE)
// *****************************

public forward_traceline(Float:v1[3], Float:v2[3], NoMonsters, shooter, ptr)
{
	if(!shooter) return FMRES_IGNORED;
	
	new hit = get_tr2(ptr,TR_pHit);
	
	if( pev(hit,pev_flags) & (FL_CLIENT | FL_FAKECLIENT) && shooter 
	&& pev(shooter,pev_flags) & (FL_CLIENT | FL_FAKECLIENT) )
	{
		new hitGroup = get_tr2(ptr,TR_iHitgroup);
		
		if( !(bodyShots[shooter][hit] & 1<<hitGroup) )
			set_tr2(ptr,TR_flFraction,1.0);
	}
	
	return FMRES_IGNORED;
}


// *****************************
// FAKEMETA INTERCEPT (NAME CHANGE)
// *****************************

public forward_infochanged(id, buffer) 
{
	if (!is_user_connected(id) || !is_flagged[id]) return FMRES_IGNORED // Is the player even flagged or better yet, on the server?
	
	static xNewName[32];
	new xID = get_user_userid(id);
	
	engfunc(EngFunc_InfoKeyValue, buffer, "name", xNewName, 31); // Retrieve the players "new" name
	
	if(get_pcvar_num(xcvar_sbuddy_antikick) != 0 && symbols_in_name(xNewName)) // Does the player have any of our restricted symbols in their name?
	{					
		server_cmd("kick #%d ^"(Remove the ` or ~ from your NAME): Anti-kick symbols are restricted!^"", xID); // Kick them and tell them why
		if(get_pcvar_num(xcvar_sbuddy_showbans) != 0)  // Should we tell the other players what just happened?
		{
			client_print(0,print_chat,"%s has been kicked for using anti-kick symbols in his name!", xNewName);
		}
		return FMRES_IGNORED;
	}
	
	
	if (equal(xNewName, forcedName[id])) // Did they change their name to the one we are going to force them to use?
	{
		return FMRES_IGNORED; // Over-ride their attempt to change the name and keep it the same
	} else
	{
		new info = engfunc(EngFunc_GetInfoKeyBuffer, id);
        	engfunc(EngFunc_SetClientKeyValue, id, info, "name", forcedName[id]); // Player tried ot change their name, so lets change it back to their forced name!
		user_slap(id,2); // Give the player a spank (damage 2hp)
		user_slap(id,3); // Why not spank the player again (damage 3hp) that should teach em lol
		
		say_green(id,"You cannot change your name: Your account is flagged!"); // Tell the player whos the boss!	   
			
		return FMRES_SUPERCEDE; // Over-ride
	}
	
	return FMRES_SUPERCEDE; // Over-ride
}


// *****************************
// FAKEMETA INTERCEPT (MICROPHONES)
// *****************************

public forward_setlisten(receiver, sender, listen) 
{
	if(receiver == sender) return FMRES_IGNORED; // The voice is being sent to the player who is sending it, so who cares, ignore the function call
		
	if(is_flagged[sender]) // Is the player flagged? If so lets not let anyone hear them!
	{
		engfunc(EngFunc_SetClientListening, receiver, sender, SPK_MUTED); // Blah Blah Blah Blah Blah, Nobody can hear you buddy! :P		
		
		return FMRES_SUPERCEDE; // Over-ride
	}
	
	if (xSpeak[sender] == SPK_ADMIN) // Is the player set to speak only on the admin channel?
	{
	
		if (is_admin[receiver]) // Is the player listening an admin? If so let them hear whats being said
		{
			engfunc(EngFunc_SetClientListening, receiver, sender, SPK_NORMAL);
		}else
		{
			engfunc(EngFunc_SetClientListening, receiver, sender, SPK_MUTED);
		}
		return FMRES_SUPERCEDE; // Over-ride
	}
	
	
	return FMRES_IGNORED; // Ignore function call and allow the voice to pass through our function
}


// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
// SUPPORT FUNCTIONS
// :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


// *****************************
// FAKEMETA SET USER HITZONES
// *****************************

stock fm_set_user_hitzones(index, target = 0, body = 255)
{
	new shooter = index;
	new gettingHit = target;
	new hitzones = body;
		
	if (!shooter && !gettingHit) 
	{
		for(new i = 0; i <= maxPlayers; i++) 
			for(new j = 0; j <= maxPlayers; j++) 
				bodyShots[i][j] = hitzones;
	}else if(!shooter && gettingHit != 0)
	{
		for(new i = 0; i <= maxPlayers; i++) 
			bodyShots[i][gettingHit] = hitzones;
	}else if (shooter != 0 && gettingHit == 0) 
	{
		for(new i = 0; i <= maxPlayers; i++) 
			bodyShots[shooter][i] = hitzones;
	}else 
	{
		if(shooter && gettingHit) bodyShots[shooter][gettingHit] = hitzones;
	}
	
	return 1;
}


// *****************************
// RETRIEVE USERS TEAM
// *****************************

stock get_user_team_id(id) 
{
    new xTeamValue[2];
    get_user_team(id, xTeamValue, 1);

    if (xTeamValue[0] == 'T') // Terrorist
        return 1;
    else if (xTeamValue[0] == 'C') // Counter-Terrorist
        return 2;
    else if (xTeamValue[0] == 'S') // Spectator
        return 3;
	
    return -1; // Nothing we are expecting. Hmmm...
}


// *****************************
// FLAG OR REQUEST CHECK (EXISTS?)
// *****************************

stock funcIsPlayerFlagged(id, xSteamToCheck[], method)
{
	new handleREAD ;
	
	switch (method)
	{
		// FLAG
		case 1:
		{
			handleREAD = fopen(xFileFlags, "rt"); // File pointer to our flagged file
		}
		
		// REQUEST
		case 2:
		{
			handleREAD = fopen(xFileRequest, "rt"); // File pointer to our requests file
		}
	}	
 
	if (!handleREAD) return -1; // A problem occured. Hmmmm....
 
	new xCurrentLine[2048], xSteam[32], xNameInFile[32], xSteamInFile[33];
	new bool:CheckBySteam = false; // Are we checking by STEAM_ID or a player thats actually in-game via UserID?
	
	if(strlen(xSteamToCheck) > 6) // Assume we are checking by STEAM_ID since the length of the arguement is more than 6 characters
	{
		CheckBySteam = true; // We are checking by STEAM_ID
		formatex(xSteam,31,"%s",xSteamToCheck); // Tell the function what we are looking for by copying our STEAM_ID to a new variable
		
		remove_quotes(xSteam); // Cleanup
		trim(xSteam); // Remove spaces on either side of the STEAM_ID (cleanup)
	} else
	{
		CheckBySteam = false; // We aren't checking by STEAM_ID
		get_user_authid(id,xSteam,31); // Let's get the players STEAM_ID then by using their UserID
	}	
	
	while (!feof(handleREAD)) // Loop until the end of our file
	{
		fgets(handleREAD, xCurrentLine, 2047); // Extract the current line from our file
		if(containi(xCurrentLine,xSteam) != -1) // Does the line in the file contain the players STEAM_ID that we are checking?
		{
			parse(xCurrentLine, xSteamInFile, 31, xNameInFile, 32); // Split the line into two seperate variables for a better comparison
			trim (xSteamInFile); // Remove spaces on either side
			trim (xNameInFile); // Remove spaces on either side
			
				// Double Check			
			if(equali(xSteamInFile,xSteam)) // Is the players STEAM_ID equal to the STEAM_ID in our flagged file?
			{	
				switch(CheckBySteam) // Are we checking via STEAM_ID?
				{
					case true:
					{
						return 1; // We are so lets assume the player isn't actually playing so just tell the calling procedure that we found a match
					}
					case false:
					{
						formatex(forcedName[id],32,"%s",xNameInFile); // We found a match, but lets also return the name so we can force the player to use it!
						return 1;
					}
				}				
			}			
		}		
	}
	
	fclose(handleREAD); // Close the file
	
	return 0;
}


// *****************************
// ADD FLAG OR REQUEST TO FILE
// *****************************

stock funcFlag(id, aid, method)
{	
	new xName[32], xSteam[32], adminSteam[32], adminName[32], xPassLine[200];
	
	get_user_name(id,xName,31); // Players name
	get_user_authid(id,xSteam,31); // Players STEAM
	
	get_user_name(aid,adminName,31); // Admins name
	get_user_authid(aid,adminSteam,31); // Admins STEAM
	
	switch (method)
	{
		// FLAG
		case 1:
		{
				/* :::::::::: CORE CODE :::::::::: */
			
			if(callfunc_begin("playerFlag","serverbuddy_core.amxx") == 1) // Call the playerFlag function in the "core"
			{     
				callfunc_push_int(id); // Pass the player ID to the function in "core"
				callfunc_push_str(xFileFlags); // Pass the file location to the function in "core"
				callfunc_end(); // Terminate call session
			} else
			{
				log_amx("Server Buddy: Unable to communicate with serverbuddy_core.amxx, Missing?"); // Log to amxx logs
				return 0; // Problem
			}
			
			is_flagged[id] = true; // Tell the plugin that the player is now flagged (plugin now knows to apply restrictions)
			formatex(forcedName[id],32,"%s",xName); // Record their current name and force them to use it from now on. (no name changes allowed)		
	
			set_hudmessage(85, 255, 255, -1.0, -1.0, 0, 6.0, 12.0); // Format a screen message to be displayed to our player
			show_hudmessage(id, "Your Account has been Flagged!"); // Tell the player they have been flagged
			
			new passID[1]; // Store the ID of our player and pass it to our timers
																
			if(!task_exists(TASK_SHOTS_ID + id)) // Make sure we don't already have a timer to punish our player (random blanks)
			{
				passID[0] = id; // Store player ID			
				set_task(2.0, "timer_SHOTS", TASK_ID + id, passID, 1, "b"); // Lets setup a personalized timer for this player to run checks on that player and apply restrictions
			} 	
		
		
				// ** TODO: Add Message Of The Day **
							
			server_cmd("sb_scroll green **FLAGGED** The Account of [%s] has been Flagged. They Cannot Speak! **FLAGGED**",xName); // Create a new scrolling msg
						
			console_print(aid,"%s has been flagged!", xName);
	
			formatex(xPassLine,199,"[FLAGGED] ---- %s ---- (%s) ---- Flagged by: %s ---- (%s)",xName,xSteam,adminName,adminSteam); // Create our log line
			log_activity(xPassLine); // Log it
		}
		
		// REQUEST
		case 2:
		{
				/* :::::::::: CORE CODE :::::::::: */
			
			if(callfunc_begin("playerRequest","serverbuddy_core.amxx") == 1) // Call the playerRequest function in the "core"
			{     
				callfunc_push_int(id); // Pass the player ID to the function in "core"
				callfunc_push_str(xFileRequest); // Pass the file location to the function in "core"
				callfunc_end(); // Terminate call session
			} else
			{
				log_amx("Server Buddy: Unable to communicate with serverbuddy_core.amxx, Missing?"); // Log to amxx logs
				return 0; // Problem
			}
			
			requested[id]=true; // Tell the plugin that the player has been asked to scan
			console_print(aid,"%s has been requested to scan! Instructions will be given to them next time they spawn.", xName);
		
			new xPassLine[200], adminSteam[32], adminName[32];
	
			get_user_name(aid,adminName,31);
			get_user_authid(aid,adminSteam,31);
	
			formatex(xPassLine,199,"[SCAN REQUEST] ---- %s ---- (%s) ---- Requested by: %s ---- (%s)",xName,xSteam,adminName,adminSteam); // Create our log line
			log_activity(xPassLine); // Log it
		}
	}	
	return 1;
}

/* xxxxxxxx OLD VERSION (BUILT INTO CORE NOW) "LEFT HERE FOR THOSE THAT MIGHT WANT IT" xxxxxxxx

stock funcFlag(id, aid, method)
{
	new handleWRITE;
	
	switch (method)
	{
		// FLAG
		case 1:
		{
			handleWRITE = fopen(xFileFlags, "a+t"); // File pointer to our flagged file
		}
		// REQUEST
		case 2:
		{
			handleWRITE = fopen(xFileRequest, "a+t"); // File pointer to our requests file
		}
	}
	
	if (!handleWRITE) return 0; // Uh oh, error opening file
	
	new xName[32], xSteam[32];
	read_argv(1, xName, 31);
	
	get_user_name(id,xName,31);
	get_user_authid(id,xSteam,31);	
	
	switch (method)
	{
		// FLAG
		case 1:
		{
			is_flagged[id]=true; // Tell the plugin that the player is now flagged (plugin now knows to apply restrictions)
			formatex(forcedName[id],32,"%s",xName); // Record their current name and force them to use it from now on. (no name changes allowed)		
	
			set_hudmessage(85, 255, 255, -1.0, -1.0, 0, 6.0, 12.0); // Format a screen message to be displayed to our player
			show_hudmessage(id, "Your Account has been Flagged!"); // Tell the player they have been flagged
		
				// ** TODO: Add Message Of The Day **
				
			server_cmd("sb_scroll green **FLAGGED** The Account of [%s] has been Flagged. They Cannot Speak! **FLAGGED**",xName); // Create a new scrolling msg
			
			console_print(aid,"%s has been flagged!", xName);
		
			new xPassLine[200], adminSteam[32], adminName[32];
	
			get_user_name(aid,adminName,31);
			get_user_authid(aid,adminSteam,31);
	
			formatex(xPassLine,199,"[FLAGGED] ---- %s ---- (%s) ---- Flagged by: %s ---- (%s) ---- <FLAG CODE=%s>",xName,xSteam,adminName,adminSteam,xSeedValue); // Create our log line
			log_activity(xPassLine); // Log it
		}
		
		// REQUEST
		case 2:
		{
			requested[id]=true; // Tell the plugin that the player has been asked to scan
			console_print(aid,"%s has been requested to scan! Instructions will be given to them next time they spawn.", xName);
		
			new xPassLine[200], adminSteam[32], adminName[32];
	
			get_user_name(aid,adminName,31);
			get_user_authid(aid,adminSteam,31);
	
			formatex(xPassLine,199,"[SCAN REQUEST] ---- %s ---- (%s) ---- Requested by: %s ---- (%s) ---- <REQUEST CODE=%s>",xName,xSteam,adminName,adminSteam,xSeedValue) // Create our log line
			log_activity(xPassLine); // Log it
		}
	}	
 	
	fprintf(handleWRITE, "%s^t^"%s^"^t0000^n", xSteam,xName); // Write the players STEAM_ID and Name to our file
		
	fclose(handleWRITE);
	
	return 1;
}
*/


// *****************************
// REMOVE FLAG OR REQUEST
// *****************************

stock funcRemoveFlag(xSteamToRemove[], id, method)
{
	if(!file_exists(xFileTemp)) delete_file(xFileTemp); // If our temp file already exists, delete it!
	if(!file_exists(xFileTempRequest)) delete_file(xFileTempRequest); // If our temp file already exists, delete it!
	
	new handleREAD;
	new handleWRITE;
		
	switch (method)
	{
		// FLAG
		case 1:
		{
			handleREAD = fopen(xFileFlags, "rt"); // File pointer to our flagged file
			handleWRITE = fopen(xFileTemp, "wt"); // File pointer to our temporary file
		}
		// REQUEST
		case 2:
		{
			handleREAD = fopen(xFileRequest, "rt"); // File pointer to our requests file
			handleWRITE = fopen(xFileTempRequest, "wt"); // File pointer to our temporary file
		}
	}
	 
	if (!handleREAD || !handleWRITE) return 0; // Uh oh, error opening the files
 
	new xCurrentLine[2048], xNameInFile[33], xSteamInFile[32];
	
	while (!feof(handleREAD)) // Loop until we reach the end of our flagged file
	{
		fgets(handleREAD, xCurrentLine, 2047); // Read the current line
		
		if(containi(xCurrentLine, xSteamToRemove) != -1 || containi(xCurrentLine, "Auto Generated Flags File") != -1)	// If we found the STEAM_ID we wish to remove or our file header then don't do anything
		{
			// Found the player we want to remove (don't add their id to the new temp file)
			parse(xCurrentLine, xSteamInFile, 31, xNameInFile, 32); // Extract the players name and STEAM_ID so we know who was unflagged
			trim(xSteamInFile); // Remove spaces on either side
			trim(xNameInFile); // Remove spaces on either side
		} else // We didn't find our STEAM_ID in the current line so it's safe to write that line to our temporary file
		{
			fprintf(handleWRITE, "%s", xCurrentLine); // Write the line to our temporary file
		}		
	}
	
	fclose(handleREAD);
	fclose(handleWRITE);
	
	new handleREADn;
	new handleWRITEn;
	
	switch (method)
	{
		// FLAG
		case 1:
		{
			delete_file(xFileFlags); // Delete our flagged file (We are about to replace it)
			handleREADn = fopen(xFileTemp, "rt"); // File pointer to our temporary file
			handleWRITEn = fopen(xFileFlags, "wt"); // File pointer to our flagged file
		}
		// REQUEST
		case 2:
		{
			delete_file(xFileRequest); // Delete our requests file (We are about to replace it)
			handleREADn = fopen(xFileTempRequest, "rt"); // File pointer to our temporary file
			handleWRITEn = fopen(xFileRequest, "wt"); // File pointer to our requests file
		}
	}
	
	if (!handleREADn || !handleWRITEn) return 0; // Uh oh, error opening the files
	
	fprintf(handleWRITEn, "// Server Buddy Auto Generated File^n");
	
	while (!feof(handleREADn)) // Loop until we reach the end of our temp file
	{
		fgets(handleREADn, xCurrentLine, 2047);	
		fprintf(handleWRITEn, "%s", xCurrentLine); // Copy our temp file line by lint to our new Flagged File			
	}
	fclose(handleREADn);
	fclose(handleWRITEn);
	
	new xPassLine[200], adminSteam[32], adminName[32]
	get_user_name(id,adminName,31)
	get_user_authid(id,adminSteam,31)
	
	switch (method)
	{
		// FLAG
		case 1:
		{				
			delete_file(xFileTemp); // Delete our temporary file
			console_print(id,"%s (%s) has been unflagged!",xNameInFile, xSteamInFile);
			new player=find_player("chl",xSteamInFile); // Is the player that matches the given STEAM_ID actually in the server?

			if (player) 
			{
				is_flagged[player]=false; // Found the player so lets tell the plugin this player isn't flagged anymore.
				fm_set_user_hitzones(id, 0, 255); // Let the player shoot again
				fm_set_rendering(player,kRenderFxNone,255,255,255, kRenderNormal,16); // Lets remove the red glow from our player
				
				if (task_exists(TASK_SHOTS_ID + player)) remove_task(TASK_SHOTS_ID + player); // Is there a timer (to punish a player) that exists for this player? If so delete it!		
	
				say_green(player,"Your account has been un-flagged! **RESTRICTIONS REMOVED**"); // Notification (green)
			}	
			
			formatex(xPassLine,199,"[UN-FLAGGED] ---- %s (%s) ---- Unflagged By: %s ---- (%s)",xNameInFile, xSteamInFile,adminName,adminSteam); // Create our log line
			log_activity(xPassLine); // Log it
		}
		
		// REQUEST
		case 2:
		{
			delete_file(xFileTempRequest); // Delete our temporary file
			console_print(id,"%s (%s) has been removed from the request list!",xNameInFile, xSteamInFile);
			
			new player=find_player("chl",xSteamInFile); // Is the player that matches the given STEAM_ID actually in the server?
			
			if (player) 
			{
				requested[player]=false; // Found the player so lets tell the plugin this player isn't flagged anymore.
				fm_set_user_hitzones(id, 0, 255); // Let the player shoot again
				fm_set_rendering(player,kRenderFxNone,255,255,255, kRenderNormal,16); // Lets remove the blue glow from our player
				
				say_green(id, "Thank you for scanning with Server Buddy your account has been un-tagged!"); // Notification (green)		
			}	

			formatex(xPassLine,199,"[REQUEST REMOVED] ---- %s (%s) ---- Removed By: %s ---- (%s)",xNameInFile, xSteamInFile,adminName,adminSteam); // Create our log line
			log_activity(xPassLine); // Log it
		}
	}	
	return 1;
}


// *****************************
// SCROLL COLORED MESSAGE
// *****************************

public scroll_msg()
{ 
		/* -------- CODE WRITTEN BY AMXMODX TEAM --------- */
		
	sc_timer -= 1 
	new b, i = 0 
	for(b = sc_start_pos; b < sc_end_pos; ++b) 
		sc_display_msg[i++] = sc_scroll_msg[b] 
	sc_display_msg[i] = 0 
	if (sc_end_pos < sc_length) 
		sc_end_pos++ 
	if (sc_x_pos > 0.35) 
		sc_x_pos -= 0.0063 
	else { 
		sc_start_pos++ 
		sc_x_pos = 0.35 
	} 
	set_hudmessage(vcolors[sc_a][0],vcolors[sc_a][1],vcolors[sc_a][2], sc_x_pos, 0.84, 0, 0.0, 1.0, 0.01, 0.01, 1) 
	show_hudmessage(0,sc_display_msg) 
} 


// *****************************
// SCROLL COLORED MESSAGE
// *****************************

public cmdSCROLL(id)
{ 	
		/* -------- CODE WRITTEN BY AMXMODX TEAM --------- */
		
	if(sc_timer > 0)
	{ 		
		return PLUGIN_HANDLED 
	} 
	 
	new length 
	new color[10]  
	read_args(sc_scroll_msg,384) 
	parse(sc_scroll_msg,color,10)  
	new found = 0 
	sc_a = 0 
	for(new i=0;i<MAX_CLR;++i)  
		if (equal(color,ncolors[i])) {  
		sc_a = i 
		found = 1 
		break  
	}  
	length = found ? strlen(color) : 0 	
	
	client_print(0,print_console, "%s",sc_scroll_msg[length])  
	
	sc_end_pos = 1 
	sc_start_pos = 0 
	sc_x_pos = 0.65 	
	 
	format(sc_scroll_msg,384,"%s",sc_scroll_msg[length]) 

	sc_length = strlen(sc_scroll_msg) 
	sc_timer = sc_length + 48 
	set_task( 0.1 , "scroll_msg",340,"",0,"a", sc_length + 48) 
	return PLUGIN_HANDLED 
} 


// *****************************
// LOG TO FILE
// *****************************

stock funcLog(xLogFile[], xLogLine[])
{
	new CurrentTime[33];
	get_time("%c",CurrentTime,32);
	
	new handleWRITE = fopen(xLogFile, "a+t"); // File pointer to our file
	if (!handleWRITE) return 0; // Uh oh, error opening file
 	
	fprintf(handleWRITE, "%s ---- %s^n", xLogLine,CurrentTime); // Write our log line with a time stamp
		
	fclose(handleWRITE); // Close our log file
	
	return 0;	
}


// *****************************
// CREATE FILE
// *****************************

stock funcCreateFile(argFilename[], argContent[])
{
	if(!file_exists(argFilename)) // If the file doesn't exist, create it!
	{
		new handleWRITE = fopen(argFilename, "wt"); // File pointer
		
		if (handleWRITE) // If we have established a connection to our file, then populate it
		{
			fprintf(handleWRITE, "%s^n", argContent); // Write the first line (simple header)				
			fclose(handleWRITE); // Close the file
		} else
		{
			return 0; // Error opening file
		}
	}
	
	return 1;
}


// *****************************
// SHOW FILE IN CONSOLE
// *****************************

stock funcShowFile(id, argFilename[])
{	
	new handleREAD = fopen(argFilename, "rt"); // File pointer
 
	if (!handleREAD) return 0; // Uh oh, error opening the file
 
	new xCurrentLine[2048], eWeb[255], xServerIP[33];

		/* -------- STORE INFORMATION TO BE REPLACED --------- */
		
	get_pcvar_string (xcvar_sbuddy_website, eWeb, 244); // Store the website address (for replacements)
	get_user_ip(0, xServerIP, 32); // Store server IP
	
	trim(xServerIP);
	
	console_cmd(id,"echo ^n^n^n"); // Enter 3 blank lines
	
	while (!feof(handleREAD)) // Loop until we reach the end of our file
	{
		fgets(handleREAD, xCurrentLine, 2047); // Read the current line
		
		if(xCurrentLine[0] == ';') continue; // Ignore comments
		if(xCurrentLine[0] == '/' && xCurrentLine[1] == '/') continue; // Ignore comments
		
		
		while( contain (xCurrentLine, "$website" ) != -1 )
			replace(xCurrentLine, 2046, "$website", eWeb); // Replace Website Info
		
		while( contain (xCurrentLine, "$contact" ) != -1 )
			replace(xCurrentLine, 2046, "$contact", owner_contact); // Replace Contact Info	
			
		while( contain (xCurrentLine, "$serverip" ) != -1 )
			replace(xCurrentLine, 2046, "$serverip", xServerIP); // Replace Server IP

		if (isHalfLife)
		{
			while( contain (xCurrentLine, "$restrictions" ) != -1 )		
				replace(xCurrentLine, 2046, "$restrictions", "crowbar"); // Replace Contact Info
		} else
		{
			while( contain (xCurrentLine, "$restrictions" ) != -1 )		
				replace(xCurrentLine, 2046, "$restrictions", "handguns or the knife"); // Replace Contact Info
		} 
	
		console_cmd(id,"echo %s", xCurrentLine); // Show the line in the players console
					
	}
	return 1;
}


// *****************************
// BROADCAST GREEN MESSAGE
// *****************************

stock funcSayGreen(id, argMsg[])
{	
	new xMsg[129];
	add(xMsg,128,"^x04"); // Add the green color flag
	add(xMsg,128,argMsg);
	
	if(id > 0 && id < 33)
	{	
		if (is_real_player[id]) // If the player isn't a bot, server or not connected
		{			
			message_begin(MSG_ONE, xSayText, {0,0,0}, id); // Message to send
			write_byte(id); // Player to send to
			write_string(xMsg); // Message
			message_end();
			
			return 1;
		}
	} else if (id == 0) // All Players
	{
		for(new i=0;i<maxPlayers;i++) // Loop through our players one by one
		{		
			if (is_real_player[i])
			{
				message_begin(MSG_ONE, xSayText, {0,0,0}, i); // Message to send
				write_byte(i); // Player to send to
				write_string(xMsg); // Message
				message_end();
			}
		}
				
		return 1;			
		
	} else if (id == 100) // Admin Only
	{
		for(new i=0;i<maxPlayers;i++) // Loop through our players one by one
		{		
			if (is_real_player[i])
			{				
				if (is_admin[i])
				{
					message_begin(MSG_ONE, xSayText, {0,0,0}, i); // Message to send
					write_byte(i); // Player to send to
					write_string(xMsg); // Message
					message_end();
				}
			}
		}
		
		return 1;		
	}
		
	return 0;
}


// *****************************
// BROADCAST CONSOLE MESSAGE TO ADMINS
// *****************************

stock funcAdminConsole(argMsg[])
{	
	for(new i=0;i<maxPlayers;i++) // Loop through our players one by one
	{		
		if (is_real_player[i])
		{				
			if (is_admin[i])
			{
				console_print(i, argMsg); // Print message
			}
		}
	}		
		
	return 0;
}


// *****************************
// CHECK NAME FOR RESTRICTED SYMBOLS
// *****************************

stock funcCheckName(argName[])
{
	if(containi(argName,"`") != -1 || containi(argName,"~") != -1) return 1; // Does the player have any of our restricted symbols in their name?
	
	return 0;
}
/* AMXX-Studio Notes - DO NOT MODIFY BELOW HERE
*{\\ rtf1\\ ansi\\ deff0{\\ fonttbl{\\ f0\\ fnil Tahoma;}}\n\\ viewkind4\\ uc1\\ pard\\ lang1033\\ f0\\ fs16 \n\\ par }
*/
